﻿#region 命名空间
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Windows;
using System.Windows.Input;
using Microsoft.CSharp;
#endregion

namespace ContentFactory
{
    /// <summary>
    /// 自定义数据编辑对话框。
    /// </summary>
    public partial class CustomDataEditDialog : Window
    {
        #region 字段

        bool _dirty;
        Dictionary<Type, Serializer> _serializers;

        #endregion

        #region 属性

        /// <summary>
        /// 数据的保存路径。
        /// </summary>
        internal string SavePath
        {
            get { return _savePath; }
            set
            {
                _savePath = value;
                Title = Path.GetFileName(_savePath);
            }
        }
        string _savePath;

        /// <summary>
        /// 自定义数据。
        /// </summary>
        public object CustomData
        {
            get { return DataContext; }
            set { DataContext = value; }
        }

        #endregion

        #region 初始化

        /// <summary>
        /// 初始化。
        /// </summary>
        public CustomDataEditDialog()
        {
            InitializeComponent();
            _serializers = new Dictionary<Type, Serializer>();
        }

        #endregion

        #region 方法

        /// <summary>
        /// 保存数据。
        /// </summary>
        internal void Save()
        {
            Type type = CustomData.GetType();
            if (!_serializers.ContainsKey(type))
            {
                Serializer serializer = new Serializer(type);
                _serializers.Add(type, serializer);
            }
            _serializers[type].Serialize(SavePath, CustomData);
            _dirty = false;
        }

        /// <summary>
        /// 加载数据。
        /// </summary>
        internal void Load()
        {
            XDocument doc = XDocument.Load(SavePath);
            XNamespace space = doc.Root.Name.Namespace;
            XElement element = doc.Root.Element(space + "Asset");
            string typeName = element.Attribute("Type").Value;
            List<Type> serializableTypes = PipelineManager.SerializableTypes;
            foreach (var type in serializableTypes)
            {
                if (type.FullName == typeName)
                {
                    if (!_serializers.ContainsKey(type))
                    {
                        Serializer serializer = new Serializer(type);
                        _serializers.Add(type, serializer);
                    }
                    CustomData = _serializers[type].Deserialize(SavePath);
                    break;
                }
            }
        }

        #endregion

        #region 事件处理

        // 数据更改后。
        private void PropertyGrid_PropertyValueChanged(object sender, Xceed.Wpf.Toolkit.PropertyGrid.PropertyValueChangedEventArgs e)
        {
            _dirty = true;
        }

        // 窗体关闭时。
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (_dirty)
            {
                MessageBoxResult result = MessageBox.Show("自定义数据已编辑，是否现在保存？", "Content Factory", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                {
                    this.Save();
                }
                else if (result == MessageBoxResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }

        #endregion

        #region 命令处理

        // 保存 命令是否可以执行。
        private void Command_Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CustomData != null && _dirty;
        }

        // 保存 命令执行。
        private void Command_Save_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.Save();
        }

        // 退出 命令是否能执行。
        private void Command_Close_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        // 退出 命令执行。
        private void Command_Close_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
        }

        #endregion
    }

    /// <summary>
    /// 序列化器。
    /// </summary>
    class Serializer
    {
        #region 字段

        MethodInfo _serializeMethod;
        MethodInfo _deSerializeMethod;

        #endregion

        #region 初始化

        /// <summary>
        /// 初始化。
        /// </summary>
        public Serializer(Type type)
        {
            CSharpCodeProvider privoder = new CSharpCodeProvider();
            ICodeCompiler compiler = privoder.CreateCompiler();
            CompilerParameters parameters = new CompilerParameters();
            parameters.ReferencedAssemblies.Add("System.dll");
            parameters.ReferencedAssemblies.Add("System.Xml.dll");
            parameters.ReferencedAssemblies.Add("Microsoft.Xna.Framework.Content.Pipeline.dll");
            parameters.ReferencedAssemblies.Add(type.Assembly.Location);
            parameters.GenerateExecutable = false;
            parameters.GenerateInMemory = true;
            StringBuilder builder = new StringBuilder();
            builder.Append("using System;");
            builder.Append(Environment.NewLine);
            builder.Append("using System.Xml;");
            builder.Append(Environment.NewLine);
            builder.Append("using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;");
            builder.Append(Environment.NewLine);
            builder.Append("namespace CustomSerializers");
            builder.Append(Environment.NewLine);
            builder.Append("{");
            builder.Append(Environment.NewLine);
            builder.AppendFormat("public static class {0}Serializer", type.Name);
            builder.Append(Environment.NewLine);
            builder.Append("      {");
            builder.Append(Environment.NewLine);
            builder.Append("          public static void Serialize(XmlWriter writer, object data)");
            builder.Append(Environment.NewLine);
            builder.Append("          {");
            builder.Append(Environment.NewLine);
            builder.AppendFormat("         IntermediateSerializer.Serialize<{0}>(writer, ({0})data, null);", type.FullName);
            builder.Append(Environment.NewLine);
            builder.Append("          }");
            builder.Append(Environment.NewLine);
            builder.Append("          public static object Deserialize(XmlReader reader)");
            builder.Append(Environment.NewLine);
            builder.Append("          {");
            builder.Append(Environment.NewLine);
            builder.AppendFormat("         return IntermediateSerializer.Deserialize<{0}>(reader, null);", type.FullName);
            builder.Append(Environment.NewLine);
            builder.Append("          }");
            builder.Append(Environment.NewLine);
            builder.Append("      }");
            builder.Append(Environment.NewLine);
            builder.Append("}");
            string code = builder.ToString();
            System.Console.WriteLine(code);
            CompilerResults result = compiler.CompileAssemblyFromSource(parameters, code);
            try
            {
                Assembly assembly = result.CompiledAssembly;
                Type serializerType = assembly.GetType(string.Format("CustomSerializers.{0}Serializer", type.Name));
                _serializeMethod = serializerType.GetMethod("Serialize");
                _deSerializeMethod = serializerType.GetMethod("Deserialize");
            }
            catch (Exception exception)
            {
                System.Console.WriteLine(exception);
            }
        }

        #endregion

        #region 方法

        /// <summary>
        /// 序列化数据。
        /// </summary>
        public void Serialize(string fileName, object data)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            using (XmlWriter writer = XmlWriter.Create(fileName, settings))
            {
                _serializeMethod.Invoke(null, new object[] { writer, data });
            }
        }

        /// <summary>
        /// 反序列化数据。
        /// </summary>
        public object Deserialize(string fileName)
        {
            object data = null;
            using (XmlReader reader = XmlReader.Create(fileName))
            {
                data = _deSerializeMethod.Invoke(null, new object[] { reader });
            }
            return data;
        }

        #endregion
    }
}